#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2
import numpy as np
import re
from typing import Dict, Optional, Tuple, List
from dataclasses import dataclass
from pathlib import Path
import datetime
import os
from PIL import Image
import time
import json
import fitz  # PyMuPDF

try:
    from rapidocr_onnxruntime import RapidOCR
    USE_RAPID_OCR = True
except ImportError:
    from paddleocr import PaddleOCR
    USE_RAPID_OCR = False

@dataclass
class InvoiceInfo:
    """发票信息数据结构"""
    invoice_type: Optional[str] = None  # 发票类型：增值税专用发票/普通发票
    deductible: Optional[bool] = None  # 是否可抵扣（专用发票为True，普通发票为False）
    invoice_number: Optional[str] = None
    invoice_date: Optional[str] = None
    buyer_name: Optional[str] = None
    buyer_tax_id: Optional[str] = None
    seller_name: Optional[str] = None
    seller_tax_id: Optional[str] = None
    total_amount: Optional[str] = None
    total_tax: Optional[str] = None
    total_with_tax: Optional[str] = None
    total_chinese: Optional[str] = None
    item_name: Optional[str] = None
    item_amount: Optional[str] = None
    tax_rate: Optional[str] = None
    issuer: Optional[str] = None
    reviewer: Optional[str] = None
    payee: Optional[str] = None
    
    def to_dict(self) -> Dict:
        return {
            'invoice_type': self.invoice_type,
            'deductible': self.deductible,
            'invoice_number': self.invoice_number,
            'invoice_date': self.invoice_date,
            'buyer_name': self.buyer_name,
            'buyer_tax_id': self.buyer_tax_id,
            'seller_name': self.seller_name,
            'seller_tax_id': self.seller_tax_id,
            'total_amount': self.total_amount,
            'total_tax': self.total_tax,
            'total_with_tax': self.total_with_tax,
            'total_chinese': self.total_chinese,
            'item_name': self.item_name,
            'item_amount': self.item_amount,
            'tax_rate': self.tax_rate,
            'issuer': self.issuer,
            'reviewer': self.reviewer,
            'payee': self.payee
        }

class InvoiceReader:
    """发票OCR识别器 - 简化版"""
    
    def __init__(self, use_fast_mode=True, max_image_size=1920):
        self.use_fast_mode = use_fast_mode
        self.max_image_size = max_image_size

        # 初始化OCR引擎 - 针对发票优化配置
        if USE_RAPID_OCR:
            # RapidOCR配置优化 - 通过配置字典方式
            config = {
                'det': {
                    'use_cuda': False,
                    'limit_side_len': 2048,  # 增加检测分辨率
                    'thresh': 0.3,           # 降低检测阈值，提高敏感度
                    'box_thresh': 0.5,       # 边界框阈值
                    'unclip_ratio': 2.0,     # 文本区域展开比例
                },
                'rec': {
                    'use_cuda': False,
                    'rec_img_shape': [3, 48, 320],  # 增加识别高度
                    'rec_batch_num': 6,
                },
                'cls': {
                    'use_cuda': False,
                    'cls_img_shape': [3, 48, 192],
                    'cls_batch_num': 6,
                    'cls_thresh': 0.9,
                }
            }

            # 尝试使用配置初始化，如果失败则使用默认配置
            try:
                self.ocr = RapidOCR()  # RapidOCR通常使用默认配置
                print("使用RapidOCR默认配置初始化成功")
            except Exception as e:
                print(f"RapidOCR初始化错误: {e}")
                self.ocr = RapidOCR()
        else:
            # PaddleOCR配置优化 - 专门针对发票文档
            self.ocr = PaddleOCR(
                use_angle_cls=True,
                use_gpu=False,
                lang='ch',
                show_log=False,
                # 文本检测参数优化
                det_db_thresh=0.05,  # 进一步降低检测阈值，提高小文本检测
                det_db_box_thresh=0.2,  # 降低边界框阈值，更敏感
                det_db_unclip_ratio=2.5,  # 优化文本区域展开比例
                det_max_side_len=6000,  # 支持更大分辨率
                det_db_score_mode='slow',  # 使用慢速但更准确的评分模式
                # 文本识别参数优化
                rec_batch_num=1,
                rec_image_shape="3, 64, 320",  # 增加识别高度，适合发票文本
                rec_algorithm='SVTR_LCNet',  # 使用更准确的识别算法
                # 系统性能参数
                enable_mkldnn=True,
                cpu_threads=4,  # 平衡性能和资源使用
                # 新增参数
                drop_score=0.3,  # 降低置信度阈值，避免丢失重要文本
                use_dilation=True,  # 启用膨胀操作，改善文本连接
            )

        # 添加备用OCR引擎（双引擎策略）
        self.backup_ocr = None
        try:
            if USE_RAPID_OCR and PaddleOCR:
                # 主引擎是RapidOCR时，备用PaddleOCR
                self.backup_ocr = PaddleOCR(
                    use_angle_cls=True, use_gpu=False, lang='ch',
                    show_log=False, det_db_thresh=0.1, drop_score=0.2
                )
            elif not USE_RAPID_OCR and RapidOCR:
                # 主引擎是PaddleOCR时，备用RapidOCR
                self.backup_ocr = RapidOCR()
        except:
            pass
    
    def pdf_to_image(self, pdf_path: str) -> List[str]:
        """将PDF转换为图像"""
        try:
            doc = fitz.open(pdf_path)
            image_paths = []
            
            for page_num in range(doc.page_count):
                page = doc[page_num]
                mat = fitz.Matrix(4.0, 4.0)  # 增加分辨率倍数，提高图像质量
                pix = page.get_pixmap(matrix=mat, alpha=False)
                
                temp_path = f"/tmp/invoice_page_{page_num}_{int(time.time())}.png"
                pix.save(temp_path)
                image_paths.append(temp_path)
            
            doc.close()
            return image_paths
            
        except Exception as e:
            raise Exception(f"PDF转换失败: {str(e)}")
    
    def preprocess_image(self, image_path: str) -> np.ndarray:
        """图像预处理 - 针对低分辨率图片优化"""
        try:
            image = cv2.imread(image_path)
            if image is None:
                raise ValueError("无法读取图像文件")

            if len(image.shape) == 3:
                gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            else:
                gray = image

            height, width = gray.shape
            print(f"原始图片尺寸: {width}x{height}")

            # 针对低分辨率图片的超分辨率放大
            min_size = 2000  # 最小分辨率阈值
            max_size = 8000  # 最大分辨率阈值

            if max(height, width) < min_size:
                # 低分辨率图片，需要放大
                scale = min_size / max(height, width)
                scale = min(scale, 4.0)  # 限制最大放大倍数为4倍
                new_width = int(width * scale)
                new_height = int(height * scale)
                print(f"低分辨率检测，放大到: {new_width}x{new_height} (放大倍数: {scale:.2f})")

                # 使用INTER_CUBIC进行高质量放大
                gray = cv2.resize(gray, (new_width, new_height), interpolation=cv2.INTER_CUBIC)

                # 对放大后的图像进行锐化处理
                kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
                gray = cv2.filter2D(gray, -1, kernel)

            elif max(height, width) > max_size:
                # 高分辨率图片，适度缩小
                scale = max_size / max(height, width)
                new_width = int(width * scale)
                new_height = int(height * scale)
                print(f"高分辨率检测，缩小到: {new_width}x{new_height}")
                gray = cv2.resize(gray, (new_width, new_height), interpolation=cv2.INTER_AREA)

            # 增强对比度 - 针对发票的浅色背景优化
            clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
            enhanced = clahe.apply(gray)

            # 自适应去噪 - 保护文字边缘
            denoised = cv2.bilateralFilter(enhanced, 9, 75, 75)

            # 多种二值化方法的组合
            # 方法1：自适应阈值
            binary1 = cv2.adaptiveThreshold(
                denoised, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 10
            )

            # 方法2：Otsu阈值
            _, binary2 = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

            # 结合两种方法的优点
            binary = cv2.bitwise_and(binary1, binary2)

            # 形态学操作优化文字结构
            kernel = np.ones((2,2), np.uint8)
            binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

            return binary

        except Exception as e:
            print(f"图像预处理错误: {str(e)}")
            # 如果预处理失败，返回简单的灰度图
            return cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    
    def enhanced_ocr_recognition(self, image_input) -> List:
        """增强OCR识别策略 - 多种策略提高识别成功率"""
        all_results = []

        # 策略1: 原图直接识别
        try:
            print("策略1: 原图直接识别...")
            if USE_RAPID_OCR:
                result, _ = self.ocr(image_input)
                if result:
                    print(f"原图识别到 {len(result)} 个文本块")
                    all_results.extend(result)
        except Exception as e:
            print(f"原图识别失败: {e}")

        # 策略2: 如果是图片文件，尝试不同的图像处理（可能识别到被遮挡的文本）
        if isinstance(image_input, str):
            try:
                print("策略2: 增强图像处理...")
                enhanced_image = self.create_enhanced_image(image_input)
                result, _ = self.ocr(enhanced_image)
                if result and len(result) > len(all_results):
                    print(f"增强图像识别到 {len(result)} 个文本块")
                    all_results = result  # 使用更好的结果
                elif result:
                    # 合并结果
                    for item in result:
                        if not any(existing[1][0] == item[1][0] for existing in all_results if len(existing) > 1 and len(item) > 1):
                            all_results.append(item)
            except Exception as e:
                print(f"增强图像识别失败: {e}")

        # 策略3: 高对比度处理（可能识别到模糊或被遮挡的文本）
        if isinstance(image_input, str):
            try:
                print("策略3: 高对比度处理...")
                high_contrast_image = self.create_high_contrast_image(image_input)
                result, _ = self.ocr(high_contrast_image)
                if result:
                    print(f"高对比度处理识别到 {len(result)} 个文本块")
                    # 合并去重
                    for item in result:
                        if not any(existing[1][0] == item[1][0] for existing in all_results if len(existing) > 1 and len(item) > 1):
                            all_results.append(item)
            except Exception as e:
                print(f"高对比度识别失败: {e}")

        print(f"总共识别到 {len(all_results)} 个文本块")
        return all_results

    def create_enhanced_image(self, image_path: str) -> np.ndarray:
        """创建增强的图像用于OCR识别"""
        import cv2
        import numpy as np

        image = cv2.imread(image_path)
        if image is None:
            raise ValueError("无法读取图像")

        # 转换为灰度图
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        # 获取图像尺寸
        height, width = gray.shape

        # 如果图像过小，进行放大
        if max(height, width) < 1500:
            scale = 1500 / max(height, width)
            new_width = int(width * scale)
            new_height = int(height * scale)
            gray = cv2.resize(gray, (new_width, new_height), interpolation=cv2.INTER_CUBIC)
            print(f"图像放大到: {new_width}x{new_height}")

        # 增强对比度
        clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
        enhanced = clahe.apply(gray)

        # 去噪
        denoised = cv2.bilateralFilter(enhanced, 9, 75, 75)

        return denoised

    def create_high_contrast_image(self, image_path: str) -> np.ndarray:
        """创建高对比度图像"""
        import cv2
        import numpy as np

        image = cv2.imread(image_path)
        if image is None:
            raise ValueError("无法读取图像")

        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        # 高对比度处理
        alpha = 2.0  # 对比度控制
        beta = -50   # 亮度控制
        high_contrast = cv2.convertScaleAbs(gray, alpha=alpha, beta=beta)

        # 二值化处理
        _, binary = cv2.threshold(high_contrast, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

        return binary

    def extract_text_from_image(self, image_path: str) -> Dict:
        """从图像中提取文本和位置信息 - 使用增强识别策略"""
        try:
            print(f"开始识别图片: {image_path}")

            # 使用增强OCR识别策略
            result = self.enhanced_ocr_recognition(image_path)

            text_blocks = []
            if result:
                for item in result:
                    try:
                        if len(item) >= 2:
                            bbox = item[0] if len(item) > 2 else None
                            text_info = item[1] if len(item) > 2 else item[1]

                            if isinstance(text_info, (list, tuple)) and len(text_info) >= 2:
                                text = text_info[0]
                                conf = text_info[1]
                            else:
                                text = str(text_info)
                                conf = item[2] if len(item) > 2 else 1.0

                            if bbox and len(bbox) >= 4:
                                # 计算中心点
                                if isinstance(bbox[0], (list, tuple)):
                                    # 四点格式 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
                                    center_x = sum([point[0] for point in bbox]) / 4
                                    center_y = sum([point[1] for point in bbox]) / 4
                                else:
                                    # 边界框格式 [x1, y1, x2, y2]
                                    center_x = (bbox[0] + bbox[2]) / 2
                                    center_y = (bbox[1] + bbox[3]) / 2
                            else:
                                center_x, center_y = 0, 0

                            text_blocks.append({
                                'text': text,
                                'confidence': conf,
                                'center': (center_x, center_y),
                                'bbox': bbox
                            })

                            # 输出识别到的文本用于调试
                            if '¥' in text or '合计' in text or '税' in text or any(c.isdigit() for c in text):
                                print(f"识别到关键文本: '{text}' (置信度: {conf:.3f})")

                    except Exception as e:
                        print(f"处理识别结果时出错: {e}, item: {item}")
                        continue

            return {
                'full_text': '\n'.join([block['text'] for block in text_blocks]),
                'blocks': text_blocks
            }

        except Exception as e:
            print(f"图像文本提取失败: {str(e)}")
            return {"full_text": "", "blocks": []}
    
    def parse_invoice_info(self, ocr_result: Dict) -> InvoiceInfo:
        """简化的发票信息提取 - 基于OCR结果的直接匹配"""
        info = InvoiceInfo()
        text_blocks = ocr_result.get('blocks', [])
        full_text = ocr_result.get('full_text', '')
        
        # 按Y坐标排序，便于上下文分析
        sorted_blocks = sorted(text_blocks, key=lambda x: x['center'][1])
        
        # 存储找到的名称和税号，用于位置关联
        names = []
        tax_ids = []
        
        for i, block in enumerate(sorted_blocks):
            text = block['text'].strip()
            center_x, center_y = block['center']

            # 0. 发票类型 - 识别增值税专用发票或普通发票
            if not info.invoice_type:
                if '增值税专用发票' in text or '专用发票' in text:
                    info.invoice_type = '增值税专用发票'
                    info.deductible = True  # 专用发票可抵扣
                elif '普通发票' in text or '普通发票' in text:
                    info.invoice_type = '普通发票'
                    info.deductible = False  # 普通发票不可抵扣

            # 1. 发票号码 - 直接匹配20位数字
            if not info.invoice_number:
                invoice_match = re.search(r'(\d{20})', text)
                if invoice_match:
                    info.invoice_number = invoice_match.group(1)
            
            # 2. 开票日期
            if not info.invoice_date:
                date_match = re.search(r'(\d{4}年\d{1,2}月\d{1,2}日)', text)
                if date_match:
                    info.invoice_date = date_match.group(1).replace('年', '-').replace('月', '-').replace('日', '')
            
            # 3. 公司名称 - 寻找"名称：公司名"格式
            if text.startswith('名称：') and len(text) > 3:
                company_name = text[3:].strip()
                if '公司' in company_name or '企业' in company_name:
                    names.append({
                        'name': company_name,
                        'position': (center_x, center_y),
                        'index': i
                    })
            
            # 4. 税号 - 匹配18位统一社会信用代码
            # 统一社会信用代码格式：
            # - 新版：18位数字+字母组合
            # - 旧版：15位或18位纯数字
            tax_match = re.search(r'([0-9A-Z]{18})', text)
            if tax_match:
                tax_id = tax_match.group(1)
                # 接受18位的税号（数字+字母组合，或纯数字）
                # 排除条件：如果包含在发票号码中（20位数字），则跳过
                if not info.invoice_number or tax_id not in info.invoice_number:
                    tax_ids.append({
                        'tax_id': tax_id,
                        'position': (center_x, center_y),
                        'index': i
                    })
            
            # 5. 金额信息 - 增强匹配逻辑
            if '¥' in text or '￥' in text:
                # 匹配所有金额（支持空格：如 "1245. 21"）
                amounts_raw = re.findall(r'[¥￥]\s*(\d+(?:\s*\.\s*\d+)?)', text)
                # 清理空格
                amounts = [amt.replace(' ', '') for amt in amounts_raw]
                
                # 特殊处理：合计行包含两个金额（金额+税额）
                if '合计' in text and '价税合计' not in text and len(amounts) == 2:
                    if not info.total_amount:
                        info.total_amount = amounts[0]
                    if not info.total_tax:
                        info.total_tax = amounts[1]
                # 价税合计
                elif '价税合计' in text and amounts:
                    if not info.total_with_tax:
                        info.total_with_tax = amounts[0]
                elif '小写' in text and amounts:
                    if not info.total_with_tax:
                        info.total_with_tax = amounts[0]
            
            # 6. 中文大写金额
            if not info.total_chinese:
                chinese_match = re.search(r'([零壹贰叁肆伍陆柒捌玖拾佰仟万亿元角分整]+)', text)
                if chinese_match and len(chinese_match.group(1)) > 3:
                    info.total_chinese = chinese_match.group(1)
            
            # 7. 税率
            if not info.tax_rate:
                rate_match = re.search(r'(\d+(?:\.\d+)?)%', text)
                if rate_match:
                    info.tax_rate = rate_match.group(1)
            
            # 8. 项目名称 - 星号格式
            if not info.item_name:
                item_match = re.search(r'\*([^*]+)\*', text)
                if item_match:
                    info.item_name = item_match.group(1)
            
            # 9. 开票人
            if text.startswith('开票人：'):
                info.issuer = text[4:].strip()
            
            # 10. 收款人
            if '收款人：' in text:
                payee_match = re.search(r'收款人：([^;；\s]+)', text)
                if payee_match:
                    info.payee = payee_match.group(1)
            
            # 11. 复核人
            if '复核人：' in text:
                reviewer_match = re.search(r'复核人：([^;；\s]+)', text)
                if reviewer_match:
                    info.reviewer = reviewer_match.group(1)
        
        # 分配购买方和销售方
        if len(names) >= 2:
            # 按X坐标分左右，一般购买方在左侧，销售方在右侧
            names_sorted = sorted(names, key=lambda x: x['position'][0])
            info.buyer_name = names_sorted[0]['name']
            info.seller_name = names_sorted[1]['name']
        elif len(names) == 1:
            # 只有一个名称，需要根据上下文判断
            name_item = names[0]
            # 查找前面的文本块，看是否有"购买方"或"销售方"标识
            for j in range(max(0, name_item['index']-3), name_item['index']):
                prev_text = sorted_blocks[j]['text']
                if '购买方' in prev_text or '购方' in prev_text:
                    info.buyer_name = name_item['name']
                    break
                elif '销售方' in prev_text or '销方' in prev_text:
                    info.seller_name = name_item['name']
                    break
        
        # 分配税号
        if len(tax_ids) >= 2:
            # 按X坐标分左右
            tax_ids_sorted = sorted(tax_ids, key=lambda x: x['position'][0])
            # 取第一个（最左边）和最后一个（最右边），避免重复税号的干扰
            info.buyer_tax_id = tax_ids_sorted[0]['tax_id']
            info.seller_tax_id = tax_ids_sorted[-1]['tax_id']
        elif len(tax_ids) == 1:
            # 只有一个税号，根据X坐标位置判断（购买方通常在左侧，销售方在右侧）
            tax_item = tax_ids[0]
            # 计算图片的大致中心X坐标
            if text_blocks:
                all_x = [block['center'][0] for block in text_blocks]
                center_x = (max(all_x) + min(all_x)) / 2

                # 如果税号在左半部分，分配给购买方；否则分配给销售方
                if tax_item['position'][0] < center_x:
                    info.buyer_tax_id = tax_item['tax_id']
                else:
                    info.seller_tax_id = tax_item['tax_id']
        
        # 全局金额分析 - 处理相邻文本块的金额
        all_amounts = []
        for block in text_blocks:
            text = block['text']
            # 匹配金额时支持空格
            amounts_raw = re.findall(r'[¥￥]\s*(\d+(?:\s*\.\s*\d+)?)', text)
            amounts = [amt.replace(' ', '') for amt in amounts_raw]
            for amount in amounts:
                all_amounts.append({
                    'amount': amount,
                    'text': text,
                    'position': block['center'],
                    'index': text_blocks.index(block)
                })
        
        # 寻找"合计"标识附近的金额（包括只有"合"字或"计"字的情况）
        for i, block in enumerate(sorted_blocks):
            text = block['text'].strip()
            if ('合计' in text and '价税合计' not in text) or text == '合' or text == '计':
                # 在这个块附近寻找两个金额（检查前后几个块）
                合计_y = block['center'][1]  # "合计"标识的Y坐标
                nearby_amounts = []
                # 检查前面5个和后面5个块
                for j in range(max(0, i-5), min(i+6, len(sorted_blocks))):
                    if j == i:  # 跳过当前块
                        continue
                    nearby_text = sorted_blocks[j]['text']
                    nearby_center = sorted_blocks[j]['center']
                    nearby_y = nearby_center[1]

                    # 匹配金额时支持空格
                    amounts_raw = re.findall(r'[¥￥]\s*(\d+(?:\s*\.\s*\d+)?)', nearby_text)
                    amounts = [amt.replace(' ', '') for amt in amounts_raw]
                    for amount in amounts:
                        # 排除价税合计的金额，只要合计相关的金额
                        if '价税合计' not in nearby_text and '小写' not in nearby_text:
                            # 只添加Y坐标相近（同一行，允许30像素误差）的金额
                            if abs(nearby_y - 合计_y) < 30:
                                nearby_amounts.append({
                                    'amount': amount,
                                    'x': nearby_center[0],
                                    'y': nearby_y,
                                    'text': nearby_text
                                })

                # 如果找到两个或更多金额，按X坐标（位置）区分：左边是合计金额，右边是税额
                if len(nearby_amounts) >= 2 and not info.total_amount and not info.total_tax:
                    # 按X坐标排序（从左到右）
                    nearby_amounts_sorted = sorted(nearby_amounts, key=lambda x: x['x'])

                    # 发票标准格式：合计金额在左，税额在右
                    amount_left = float(nearby_amounts_sorted[0]['amount'])
                    amount_right = float(nearby_amounts_sorted[1]['amount'])

                    # 验证：合计金额应该远大于税额（税率通常1%-13%）
                    # 如果左边金额 > 右边金额的5倍，说明格式正确
                    if amount_left > amount_right * 5:
                        info.total_amount = nearby_amounts_sorted[0]['amount']
                        info.total_tax = nearby_amounts_sorted[1]['amount']
                    # 如果右边金额 > 左边金额的5倍，说明位置可能相反（少见）
                    elif amount_right > amount_left * 5:
                        info.total_amount = nearby_amounts_sorted[1]['amount']
                        info.total_tax = nearby_amounts_sorted[0]['amount']
                    # 如果两个金额差距不大，按大小判断（兜底逻辑）
                    else:
                        if amount_left > amount_right:
                            info.total_amount = nearby_amounts_sorted[0]['amount']
                            info.total_tax = nearby_amounts_sorted[1]['amount']
                        else:
                            info.total_amount = nearby_amounts_sorted[1]['amount']
                            info.total_tax = nearby_amounts_sorted[0]['amount']
                    break
                elif len(nearby_amounts) == 1 and not info.total_amount:
                    info.total_amount = nearby_amounts[0]['amount']
        
        # 如果还缺失税额，通过计算获得
        if not info.total_tax and info.total_amount and info.total_with_tax:
            try:
                total_amount = float(info.total_amount)
                total_with_tax = float(info.total_with_tax)
                calculated_tax = round(total_with_tax - total_amount, 2)
                # 验证这个计算结果是否在识别的金额中
                for amount_info in all_amounts:
                    if abs(float(amount_info['amount']) - calculated_tax) < 0.01:
                        info.total_tax = amount_info['amount']
                        break
                if not info.total_tax:
                    info.total_tax = str(calculated_tax)
            except:
                pass
        
        # 补充缺失的中文大写金额
        if not info.total_chinese:
            for block in text_blocks:
                text = block['text']
                # 更宽松的中文大写金额匹配
                chinese_match = re.search(r'([零壹贰叁肆伍陆柒捌玖拾佰仟万亿]+[元圆][整角分]*)', text)
                if chinese_match:
                    info.total_chinese = chinese_match.group(1)
                    break
        
        return info
    
    def read_invoice(self, file_path: str) -> InvoiceInfo:
        """读取发票信息"""
        try:
            if file_path.lower().endswith('.pdf'):
                image_paths = self.pdf_to_image(file_path)
                all_ocr_result = {"full_text": "", "blocks": []}
                
                for image_path in image_paths:
                    try:
                        ocr_result = self.extract_text_from_image(image_path)
                        all_ocr_result["full_text"] += ocr_result["full_text"] + "\n"
                        all_ocr_result["blocks"].extend(ocr_result["blocks"])
                    finally:
                        if os.path.exists(image_path):
                            os.remove(image_path)
                
                return self.parse_invoice_info(all_ocr_result)
            else:
                ocr_result = self.extract_text_from_image(file_path)
                return self.parse_invoice_info(ocr_result)
                
        except Exception as e:
            print(f"发票识别错误: {str(e)}")
            return InvoiceInfo()
    
    def validate_invoice_info(self, info: InvoiceInfo) -> Dict:
        """验证发票信息"""
        validation = {"valid": True, "errors": [], "warnings": []}

        # 发票类型检查 - 重要：区分可抵扣和不可抵扣发票
        if info.invoice_type:
            if info.invoice_type == '普通发票':
                validation["warnings"].append("此为普通发票，不可用于进项税额抵扣")
                validation["deductible"] = False
            elif info.invoice_type == '增值税专用发票':
                validation["deductible"] = True
        else:
            validation["warnings"].append("未能识别发票类型，请人工核验")

        # 基本字段检查
        if not info.invoice_number:
            validation["errors"].append("缺少发票号码")
            validation["valid"] = False
        elif not re.match(r'^\d{20}$', info.invoice_number):
            validation["errors"].append("发票号码格式不正确")
            validation["valid"] = False

        if not info.invoice_date:
            validation["errors"].append("缺少开票日期")
            validation["valid"] = False

        if not info.buyer_name:
            validation["errors"].append("缺少购买方名称")
            validation["valid"] = False

        if not info.seller_name:
            validation["errors"].append("缺少销售方名称")
            validation["valid"] = False

        if not info.total_with_tax:
            validation["errors"].append("缺少价税合计")
            validation["valid"] = False

        # 税号格式检查
        if info.buyer_tax_id and not re.match(r'^[A-Z0-9]{18}$', info.buyer_tax_id):
            validation["errors"].append("购买方税号格式不正确")
            validation["valid"] = False

        if info.seller_tax_id and not re.match(r'^[A-Z0-9]{18}$', info.seller_tax_id):
            validation["errors"].append("销售方税号格式不正确")
            validation["valid"] = False

        # 避免购买方和销售方相同
        if info.buyer_name and info.seller_name and info.buyer_name == info.seller_name:
            validation["errors"].append("购买方和销售方不能为同一实体")
            validation["valid"] = False

        return validation